home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 7 / BBS in a Box - Macintosh - Volume VII (BBS in a Box) (January 1993).iso / Files / Prog / N-P / OOP for C.cpt / Flavors4C.ƒ / life.c < prev    next >
C/C++ Source or Header  |  1988-12-12  |  15KB  |  514 lines

  1. /****************************************************************************/
  2. /*                          Flavors4C Life example                          */
  3. /*            © Copyright 1988 Peter Ohler,  All Rights Reserved            */
  4. /****************************************************************************/
  5. /*
  6.  
  7. This file is an example of how to use some of the functions in Flavors4C.
  8. This example is not meant to be an example of good programming style or of
  9. the most efficient way to implement the sample problem.
  10.  
  11. The task that this example attempts to perform is to model a closed
  12. ecological system using object oriented code.  In this simple eco-system, the
  13. environment consists of a two dimensional array which is inhabited by the
  14. objects or creatures that make up the eco-system.  Each creature can move and
  15. can interact with other creatures in certain ways.  The creatures defined in
  16. the following code are Rabbits, Coyotes, and Road Runners.  Each of these
  17. creatures has some common behavior that is shared through the inherited
  18. flavors.  Each one also has some special behavior that it inherites from some
  19. "mixin" flavors.  In order to get things moving a clock is used to broadcast
  20. a message to every object that tells the object to perform the "action" for
  21. that object.  The "action" for each is how the creature reacts to it's
  22. surrounding.
  23.  
  24. The programmer is encouraged to expand and modify this example in order to
  25. develop a better understanding of how Flavors4C is used.
  26. */
  27.  
  28. /*    The "string", "unix", "storage", and "stdio" libraries are needed for the
  29.     evaluation version of Flavors4C.    */
  30.  
  31. /*    The following libraries should be included in the LIFE.Project using
  32.     THINK C: MacTraps, stdio, storage, strings, Flavors4C, math, unix    */
  33.     
  34. #include <stdio.h>    /* required to use Flavors4C */
  35. #include <unix.h>    /* required to use Flavors4C */
  36. #include "Flavor4C.h"
  37.  
  38. #define ROWS            8
  39. #define COLS            18
  40. #define DURATION        10
  41. #define COYOTES            6
  42. #define RABBITS            3
  43. #define ROAD_RUNNERS    6
  44. #define COL_OFFSET        60
  45.  
  46. #define    NULL    0
  47.  
  48. Instance    **world;
  49.  
  50. /* spot() is used to see if something is in the (row, col) of the world
  51. array.  The returned value is a pointer to the (row, col) so that the value
  52. can be changed easily.
  53. */
  54. Instance**
  55. spot(row, col)
  56.     int        row, col;
  57. {
  58.     return &(world[row*COLS + col]);
  59. }
  60.  
  61. main()
  62. {
  63.     int    r, c;
  64.     Instance    **op;
  65.     
  66.     makeWorld();
  67.     gotoxy(0,0);
  68.     makeClasses();
  69.     makeCreatures();
  70.     startClock();
  71.     gotoxy(0,ROWS+2);
  72. }
  73.  
  74. /* This function allocates the memory for the world array and then draws the
  75. board on the screen.
  76. */
  77. makeWorld()
  78. {
  79.     int            r, c;
  80.     Instance    **op;
  81.     char        *malloc();
  82.     
  83.     if ((world = (Instance**)malloc(sizeof(Instance*)*ROWS*COLS)) == NULL) {
  84.         printf("Could not allocate memory for the world array!");
  85.         exit(0);
  86.     }
  87.     for(r=0; r<ROWS; r++) {
  88.         for(c=0; c<COLS; c++) {
  89.             *spot(r, c) = NULL;
  90.         }
  91.     }
  92.     gotoxy(0 + COL_OFFSET,0);
  93.     for(c=0; c<COLS+2; c++) printf("-");
  94.     for(r=1; r<=ROWS; r++) {
  95.         gotoxy(0 + COL_OFFSET,r);
  96.         printf("|");
  97.         for(c=0; c<COLS; c++) printf(" ");
  98.         printf("|");
  99.     }
  100.     gotoxy(0 + COL_OFFSET,ROWS+1);
  101.     for(c=0; c<COLS+2; c++) printf("-");
  102. }
  103.  
  104. /* This creates all the classes for the objects. */
  105. makeClasses()
  106. {
  107.     crThing();
  108.     crMobile();
  109.     crSpeedy();
  110.     crRepro();
  111.     crPredator();
  112.     crCoyote();
  113.     crRabbit();
  114.     crRunner();
  115. }
  116.  
  117. /* This starts the action and continues it for the duration. */
  118. startClock()
  119. {
  120.     int        t;
  121.     char    c = 'P';
  122.     
  123.     for(t=0; t<DURATION; t++) {
  124.         BroadCast("Thing", "action");
  125.     }
  126. }
  127.  
  128. /* The object drawing routine.  Since all objects should be drawn on the
  129. screen this function is used whenever the object must be drawn.  It is
  130. attached, as a method to the "Thing" flavor which all objects are built on in
  131. this system.  The function determines where the object is to be drawn from
  132. the row and col instance variables of the object and then it draws the
  133. character which represents the type of object that must be drawn.  The symbol
  134. variable of each flavor is used as the character that is drawn on the screen.
  135. Since all objects of a given type should look alike the symbol variable is
  136. defined as a class variable.  When the object is drawn the world array is
  137. updated so that the world array will always be consistant with the display.
  138. */
  139. Pntr
  140. draw_thing(self)
  141.     Instance    *self;
  142. {
  143.     int        row, col;
  144.     
  145.     col = *(int*)GetVar(self, "col");
  146.     row = *(int*)GetVar(self, "row");
  147.     gotoxy(col + 1 + COL_OFFSET, row + 1);
  148.     printf("%c", *(char*)GetVar(self, "symbol"));
  149.     *spot(row, col) = self;
  150.     return (Pntr)self;
  151. }
  152.  
  153. /* The object erasing routine.  This method is the inverse of the draw method
  154. described above.
  155. */
  156. Pntr
  157. erase_thing(self)
  158.     Instance    *self;
  159. {
  160.     int        row, col;
  161.     
  162.     col = *(int*)GetVar(self, "col");
  163.     row = *(int*)GetVar(self, "row");
  164.     gotoxy(col + 1 + COL_OFFSET, row + 1);
  165.     printf(" ");
  166.     *spot(row, col) = NULL;
  167.     return NULL;    
  168. }
  169.  
  170. /* This creates the Flavor for a basic "Thing". */
  171. crThing()
  172. {
  173.     Flavor    *thing;  /* use of a pointer is faster than using a string */
  174.     
  175.     thing = DefFlavor("Thing", "");
  176. /* The row and col instance varaibles are added to the flavor "Thing" and the
  177. default values are set to -1. */
  178.     AddInstanceVar(thing, "row", ALL, int, -1);
  179.     AddInstanceVar(thing, "col", ALL, int, -1);
  180. /* After a "Thing" is created the object must be placed in the array and
  181. drawn on the screen. */
  182.     DefMethod(thing, AFTER, "init", draw_thing);
  183. /* Before we remove an object it must be removed from the array and erased. */
  184.     DefMethod(thing, BEFORE, "kill", erase_thing);
  185. /* Since the clock will broadcast to all instances of "Thing" we want to make
  186. sure that the flavor has a method for "action" when it is called. */
  187.     RequiredMethods(thing, "action");
  188. /* We would like all the inherited actions to be performed for each object,
  189. as well as the before, after, and whoppers.  The DAEMON_OR combination will
  190. allow this behavior. */
  191.     MethodCombination("Thing", DAEMON_OR, BASE_FLAVOR_LAST, "action");
  192. /* Complete the definition of "Thing" by compiling the flavor.  Keeping all
  193. the definition for "Thing" in one function will avoid confusion. */
  194.     CompileFlavor(thing);
  195. }
  196.  
  197. /* This function is used to determine which way a mobile thing will move.  We
  198. would like random changes in direction when an object can not continune on
  199. it's current course.  First all the possible directions are checked.  Any
  200. adjoining space that is empty is stored in a set of arrays.  The new
  201. direction is selected by randomly by picking an index into the array at
  202. random. */
  203. int
  204. pick_dir(row, col, dr, dc)
  205.     int        row, col, *dr, *dc;
  206. {
  207.     int        r, c, newr, newc;
  208.     int        xr[9], xc[9], i, pick;
  209.     
  210.     i = 0;
  211.     for(r=-1; r<2; r++) {
  212.         for(c=-1; c<2; c++) {
  213.             if ((!r && !c) || (r == *dr && c == *dc)) continue;
  214.             newr = (row + r + ROWS) % ROWS;
  215.             newc = (col + c + COLS) % COLS;
  216.             if (*spot(newr, newc) == NULL) {
  217.                 xr[i] = r;
  218.                 xc[i++] = c;
  219.             }
  220.         }
  221.     }
  222.     if (i > 0) {
  223.         pick = rand() % i;
  224.         *dr = xr[pick];
  225.         *dc = xc[pick];
  226.     }
  227.     return i;  /* used to indicate success */
  228. }
  229.  
  230. /* This is the primary "action" method for the Mobile_Mixin.  Any object that
  231. will move should have this method included.  It is attached to the
  232. Mobile_mixin flavor.  An attempt to move in the current direction is always
  233. tried first.  If the adjacent space for the move is not empty then a new
  234. direction is chosen. */
  235. Pntr
  236. move_it(self)
  237.     Instance    *self;
  238. {
  239.     int        *dr, *dc, *row, *col, newr, newc;
  240.     Instance    **newpos;
  241.     
  242.     dr = (int*)GetVar(self, "dir-row");
  243.     dc = (int*)GetVar(self, "dir-col");
  244.     row = (int*)GetVar(self, "row");
  245.     col = (int*)GetVar(self, "col");
  246.     newr = (*row + *dr + ROWS) % ROWS;
  247.     newc = (*col + *dc + COLS) % COLS;
  248.     newpos = spot(newr, newc);
  249.     if ((*dr != 0 || *dr != 0) && *newpos == NULL) {
  250.         *row = newr;
  251.         *col = newc;
  252.     } else if (pick_dir(*row, *col, dr, dc)) {
  253.         *row = (*row + *dr + ROWS) % ROWS;
  254.         *col = (*col + *dc + COLS) % COLS;
  255.     }
  256.     return NULL;
  257. }
  258.  
  259. /* This creates the Flavor for a "Mobile_mixin". */
  260. crMobile()
  261. {
  262.     DefFlavor("Mobile_mixin", "");
  263. /* dir-row and dir-col are used to remember the last direction that the
  264. object was moving */
  265.     AddInstanceVar("Mobile_mixin", "dir-row", ALL, int, 0);
  266.     AddInstanceVar("Mobile_mixin", "dir-col", ALL, int, 0);
  267. /* Since any mobile object must have the features found in "Thing", "Thing"
  268. is made a required flavor. */
  269.     RequiredFlavors("Mobile_mixin", "Thing");
  270. /* Before an object is moved we must erase it from it's current position and
  271. then after it is moved we can redraw it. */
  272.     DefMethod("Mobile_mixin", BEFORE, "action", erase_thing);
  273.     DefMethod("Mobile_mixin", PRIMARY, "action", move_it);
  274.     DefMethod("Mobile_mixin", AFTER, "action", draw_thing);
  275.     CompileFlavor("Mobile_mixin");
  276. }
  277.  
  278. /* This is the Whopper for the "Speedy_mixin".  The Speedy_Mixin causes the
  279. action of the object to be executed twice, no matter what the action is.
  280. This behavior is created using a Whopper.  Note the two arguments required by
  281. a function that is used as a Whopper. */
  282. Pntr
  283. stutter(methods, self)
  284.     Pntr    methods;
  285.     Instance    *self;
  286. {
  287.     ContinueWhopper(methods, self);
  288.     return ContinueWhopper(methods, self);
  289. }
  290.  
  291. /* This creates the Flavor for a "Speedy_mixin". */
  292. crSpeedy()
  293. {
  294.     DefFlavor("Speedy_mixin", "");
  295. /* Lets make the Mobile_mixin included whenever the Speedy_mixin is used. */
  296.     IncludedFlavors("Speedy_mixin", "Mobile_mixin");
  297.     DefMethod("Speedy_mixin", WHOPPER, "action", stutter);
  298.     CompileFlavor("Speedy_mixin");
  299. }
  300.  
  301. /* This function is used to find a mate for things that can reproduce.  First
  302. each spot around the object is checked.  If one of the spots is empty and one
  303. of the spots has an object of the same class then a true is returned.
  304. (interbreeding amoung different species is not allowed) */
  305. int
  306. find_mate(kind, row, col, kidr, kidc)
  307.     Flavor    *kind;
  308.     int        row, col, *kidr, *kidc;
  309. {
  310.     int            r, c, found, newr, newc;
  311.     Instance    *mate;
  312.  
  313.     *kidr = row;
  314.     *kidc = col;
  315.     found = 0;
  316.     for(r=-1; r<2; r++) {
  317.         for(c=-1; c<2; c++) {
  318.             if (!r && !c) continue;
  319.             newr = (row + r + ROWS) % ROWS;
  320.             newc = (col + c + COLS) % COLS;
  321.             if ((mate = *spot(newr, newc)) == NULL) {
  322.                 *kidr = newr;
  323.                 *kidc = newc;
  324.             } else if (!Send(mate, "has-flavor", kind)) {
  325.                 found = 1;
  326.             }
  327.         }
  328.     }
  329.     if (found && (*kidr != row || *kidc != col)) {
  330.         return 1;
  331.     }
  332.     return 0;
  333. }
  334.  
  335. /* This is the method that allows things to reproduce.  First check for a
  336. mate and if possible create a new instance of the same type. */
  337. Pntr
  338. mate(self)
  339.     Instance    *self;
  340. {
  341.     int            row, col, kidr, kidc;
  342.     Instance    *kid;
  343.     Flavor        *flavor;
  344.     
  345.     row = *(int*)GetVar(self, "row");
  346.     col = *(int*)GetVar(self, "col");
  347.     flavor = (Flavor*)Send(self, "instance-of");
  348.     if (find_mate(flavor, row, col, &kidr, &kidc)) {
  349.         kid = MakeInstance(flavor, 0);
  350.         *(int*)GetVar(kid, "row") = kidr;
  351.         *(int*)GetVar(kid, "col") = kidc;
  352.         Send(kid, "init");
  353.     }
  354.     return NULL;
  355. }
  356.  
  357. /* This creates the Flavor for a "Reproduce_mixin". */
  358. crRepro()
  359. {
  360.     DefFlavor("Reproduce_mixin", "");
  361.     RequiredFlavors("Reproduce_mixin", "Thing");
  362.     DefMethod("Reproduce_mixin", PRIMARY, "action", mate);
  363.     CompileFlavor("Reproduce_mixin");
  364. }
  365.  
  366. /* This functions finds food for things that eat other things.  Food is found
  367. be checking the spots around the object.  If one is occupied by some other
  368. type of object then that spot is returned as the meal.  The kind of object is
  369. checked so that we don't have any canabalism.  Food is chosen at random so as
  370. not to be biased in one direction.  Also the object will move toward the meal
  371. since the direction is updated.  */
  372. Instance*
  373. find_food(kind, row, col, dr, dc)
  374.     Flavor    *kind;
  375.     int        row, col, *dr, *dc;
  376. {
  377.     int            r, c, newr, newc;
  378.     int            xr[9], xc[9], i, pick;
  379.     Instance    *meal, *xmeal[9];
  380.  
  381.     i = 0;
  382.     for(r=-1; r<2; r++) {
  383.         for(c=-1; c<2; c++) {
  384.             if (!r && !c) continue;
  385.             newr = (row + r + ROWS) % ROWS;
  386.             newc = (col + c + COLS) % COLS;
  387.             if ((meal = *spot(newr, newc)) != NULL &&
  388.                 !Send(meal, "has-flavor", kind)) {
  389.                 xmeal[i] = meal;
  390.                 xr[i] = r;
  391.                 xc[i++] = c;
  392.             }
  393.         }
  394.     }
  395.     if (i > 0) {
  396.         pick = rand() % i;
  397.         *dr = xr[pick];
  398.         *dc = xc[pick];
  399.         return xmeal[pick];
  400.     }
  401.     return NULL;
  402. }
  403.  
  404. /* This is the method that allows predators to eat other things.  If a meal
  405. is found then it is sent a kill message and it is removed from the world and
  406. deleted. */
  407. Pntr
  408. eat(self)
  409.     Instance    *self;
  410. {
  411.     int            *dr, *dc, *row, *col, newr, newc;
  412.     Instance    *meal;
  413.     
  414.     dr = (int*)GetVar(self, "dir-row");
  415.     dc = (int*)GetVar(self, "dir-col");
  416.     row = (int*)GetVar(self, "row");
  417.     col = (int*)GetVar(self, "col");
  418.     if (meal = find_food(Send(self, "instance-of"), *row, *col, dr, dc))
  419.         Send(meal, "kill");
  420.     return NULL;
  421. }
  422.  
  423. /* This creates the Flavor for a "Predator_mixin". */
  424. crPredator()
  425. {
  426.     DefFlavor("Predator_mixin", "");
  427.     RequiredFlavors("Predator_mixin", "Thing");
  428.     DefMethod("Predator_mixin", PRIMARY, "action", eat);
  429.     CompileFlavor("Predator_mixin");
  430. }
  431.  
  432. crCoyote()
  433. {
  434. /* Define what messages this object will respond to. */
  435.     DefFlavor("Coyote", "Predator_mixin Mobile_mixin Thing");
  436. /* Reset the symbol instance variable for this type of object. */
  437.     AddClassVar("Coyote", "symbol", ALL, char, '∑');
  438.     MethodCombination("Coyote", DAEMON_OR, BASE_FLAVOR_LAST, "action");
  439.     CompileFlavor("Coyote");
  440. }
  441.  
  442. crRabbit()
  443. {
  444. /* Define what messages this object will respond to. */
  445.     DefFlavor("Rabbit", "Reproduce_mixin Mobile_mixin Thing");
  446. /* Reset the symbol instance variable for this type of object. */
  447.     AddClassVar("Rabbit", "symbol", ALL, char, '¥');
  448.     MethodCombination("Rabbit", DAEMON_OR, BASE_FLAVOR_LAST, "action");
  449.     CompileFlavor("Rabbit");
  450. }
  451.  
  452. crRunner()
  453. {
  454. /* Define what messages this object will respond to. */
  455.     DefFlavor("Road_Runner", "Speedy_mixin Mobile_mixin Thing");
  456. /* Reset the symbol instance variable for this type of object. */
  457.     AddClassVar("Road_Runner", "symbol", ALL, char, 'Ω');
  458.     MethodCombination("Road_Runner", DAEMON_OR, BASE_FLAVOR_LAST, "action");
  459.     CompileFlavor("Road_Runner");
  460. }
  461.  
  462. /* This function will create all the instances of each Flavor.  First seed
  463. the random number generator based on the time.  Then for every creation of a
  464. creature find an unoccupied spot and place the object in the spot by setting
  465. the row and col of the object.  Note that the init flag for MakeInstance() is
  466. 0 so that the init method will be delayed until explicitly sent or until the
  467. first a message is sent to the object. */
  468. makeCreatures()
  469. {
  470.     int            i, r, c, cnt, *dr, *dc;
  471.     Instance    **pos, *obj;
  472.     
  473.     srand((unsigned)(time(NULL) % 10000 + 1));
  474.     for(i=0; i<COYOTES; i++) {
  475.         for(cnt=0; cnt<ROWS*COLS; cnt++) {
  476.                             /* a limit on attempts to find a clear spot. */
  477.             r = rand() % ROWS;
  478.             c = rand() % COLS;
  479.             if (*spot(r, c) == NULL) {
  480.                 obj = MakeInstance("Coyote", 0);
  481.                 *(int*)GetVar(obj, "row") = r;
  482.                 *(int*)GetVar(obj, "col") = c;
  483.                 Send(obj, "init");   /* just to see where the coyotes are */
  484.                 break;
  485.             }
  486.         }
  487.     }
  488.     for(i=0; i<RABBITS; i++) {
  489.         for(cnt=0; cnt<ROWS*COLS; cnt++) {
  490.             r = rand() % ROWS;
  491.             c = rand() % COLS;
  492.             if (*spot(r, c) == NULL) {
  493.                 obj = MakeInstance("Rabbit", 0);
  494.                 *(int*)GetVar(obj, "row") = r;
  495.                 *(int*)GetVar(obj, "col") = c;
  496.                 break;
  497.             }
  498.         }
  499.     }
  500.     for(i=0; i<ROAD_RUNNERS; i++) {
  501.         for(cnt=0; cnt<ROWS*COLS; cnt++) {
  502.             r = rand() % ROWS;
  503.             c = rand() % COLS;
  504.             if (*spot(r, c) == NULL) {
  505.                 obj = MakeInstance("Road_Runner", 0);
  506.                 *(int*)GetVar(obj, "row") = r;
  507.                 *(int*)GetVar(obj, "col") = c;
  508.                 break;
  509.             }
  510.         }
  511.     }
  512. }
  513.  
  514.